<template>
  <form class="tui-form" :class="{ 'tui-form--inline': props.inline }" @submit.prevent="handleSubmit">
    <slot></slot>
  </form>
</template>

<script lang="ts" setup>
import { ref, nextTick, provide, watch, reactive, computed, Ref } from 'vue';
import { TuiFormProps } from './types';
import { validateField, validateFields } from '../../validators/validators';
import { alignElements, resetElementWidths } from '../../dom/helpers';

const props = withDefaults(defineProps<TuiFormProps>(), {
  model: () => ({}),
  rules: () => ({}),
  inline: false,
  alignLabels: false,
});

const labelElements = reactive<Set<HTMLElement>>(new Set());
const elementLabelWidths = new Map<HTMLElement, string | number>();

provide('registerLabel', (element: HTMLElement, labelWidth: string | number = 'auto') => {
  try {
    if (element && element instanceof HTMLElement) {
      // 存储元素及其对应的labelWidth
      elementLabelWidths.set(element, labelWidth);

      // 如果元素不存在于集合中，添加它
      if (!labelElements.has(element)) {
        labelElements.add(element);
      }
      // 重新注册label时会先更新为新的宽度，再根据alignLabels是否为true来决定是否重新计算宽度
      resetElementWidths(elementLabelWidths);
      if (props.alignLabels) {
        alignElements(Array.from(labelElements));
      }
    } else {
      console.warn('Form: Invalid element provided to registerLabel');
    }
  } catch (error) {
    console.error('Form: Error registering label element:', error);
  }
});

// 响应式的表单错误信息对象，使用更具体的类型
const formErrors = ref<Record<string, string[]>>({});

// 提供表单错误信息给子组件，添加更好的类型定义
provide<Ref<Record<string, string[]>>>('formErrors', formErrors);

// 提供单个字段验证方法给子组件，增强错误处理和类型安全
provide('validateField', (field: string, value: any): string[] => {
  try {
    // 字段名必须是字符串
    if (typeof field !== 'string') {
      console.warn('Form: validateField requires a string field name');
      return [];
    }

    // 如果没有设置验证规则或该字段没有规则，默认通过验证
    if (!props.rules || !props.rules[field]) {
      // 清理该字段可能存在的错误信息
      if (formErrors.value[field]) {
        delete formErrors.value[field];
      }
      return [];
    }

    const fieldRules = props.rules[field];
    const fieldErrors = validateField(value, fieldRules);

    // 更新表单错误信息
    if (fieldErrors.length > 0) {
      formErrors.value[field] = fieldErrors;
    } else {
      delete formErrors.value[field];
    }

    return fieldErrors;
  } catch (error) {
    console.error(`Form: Error validating field '${field}':`, error);
    return [];
  }
});

// 监听alignLabels的变化
watch(() => props.alignLabels, (newValue) => {
  try {
    if (false === newValue) {
      // 当alignLabels变为false时，根据每个元素的labelWidth重新设置宽度
      resetElementWidths(elementLabelWidths);
    } else {
      // 当alignLabels变为true时，重新对齐标签宽度
      nextTick(() => {
        alignElements(Array.from(labelElements));
      });
    }
  } catch (error) {
    console.error('Form: Error updating label alignment:', error);
  }
}, { immediate: true });

const emit = defineEmits<{
  (e: 'submit', data: Record<string, any>): void;
  (e: 'validate', data: Record<string, any>, isValid: boolean, errors?: Record<string, string[]>): void;
  (e: 'error', error: Error, context?: string): void;
}>();

// 使用computed获取表单数据，避免创建不必要的ref
const formData = computed(() => props.model || {});

// 表单提交处理
const handleSubmit = async () => {
  try {
    // 执行表单验证
    const isValid = await validateForm();
    if (isValid) {
      emit('submit', formData.value);
    }
  } catch (error) {
    console.error('Form: Error during form submission:', error);
    emit('error', error as Error, 'submit');
  }
};

// 表单验证 - 整合使用 validateFields 函数减少重复代码
const validateForm = (): boolean => {
  try {
    // 如果没有设置验证规则，默认通过验证
    if (!props.rules || Object.keys(props.rules).length === 0) {
      formErrors.value = {};
      emit('validate', formData.value, true);
      return true;
    }

    // 使用 validateFields 函数验证所有字段，避免重复代码
    const errors = validateFields(formData.value, props.rules);
    const isValid = Object.keys(errors).length === 0;

    // 更新表单错误信息
    formErrors.value = errors;

    // 触发验证事件，传递表单数据、验证结果和错误信息
    emit('validate', formData.value, isValid, errors);
    return isValid;
  } catch (error) {
    console.error('Form: Error during form validation:', error);
    emit('error', error as Error, 'validation');
    formErrors.value = {};
    return false;
  }
};

</script>

<style scoped lang="scss">
.tui-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  border-radius: 8px;
  background-color: var(--tui-color-white);

  &--inline {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 20px;
    padding: 10px;

  }
}
</style>